REST API 설계 시 표준 가이드라인 | 매거진에 참여하세요

questTypeString.01quest1SubTypeString.04
publish_date : 25.03.07

REST API 설계 시 표준 가이드라인

#API설계 #가이드라인 #보안 #캐시 #로깅 #모니터링 #권한 #호환성 #응답코드 #메서드

content_guide

오늘날 상호 연결된 소프트웨어 생태계에서 REST API는 모듈화되고 확장 가능하며 유지보수가 쉬운 인터페이스를 제공함으로써 다양한 시스템과 애플리케이션 간의 통신을 가능하게 합니다.

그러나 잘못 설계된 API는 비효율적인 통신, 일관성 없는 동작, 그리고 API 사용자들의 불만을 초래할 수 있습니다.

이 글은 시간이 지나도 변함없이 견고한 REST API를 설계하기 위한 주요 모범 사례에 대해 논의합니다.

이 글에서 논의된 모범 사례 중 일부는 Roy Fielding(또는 그의 논문)의 직접적인 권장 사항은 아닙니다.

대신, 이들은 개인적인 경험을 통해 배우고 권위 있는 출처로 검증된 실용적인 전략들을 모아놓은 것입니다.

일관된 명명 규칙과 URL 구조 사용


  1. RFC 3986은 웹 전반에 걸쳐 일관성, 명확성 및 상호 운용성을 극대화하기 위해 URI(Uniform Resource Identifiers)의 구문과 의미를 설명합니다.

  2. 이 RFC에서 다음과 같은 모범 사례를 추출할 수 있습니다

    • URI의 구조는 해당 리소스 간의 논리적 관계를 반영해야 합니다.

    • 계층적으로 배열된 경로(예: /users/{userId}/orders)는 리소스와 그 의존성에 대한 의미 있는 컨텍스트를 전달하는 데 도움이 됩니다.

    • 임의적이거나 평평한 구조는 의미적 명확성을 흐리므로 피해야 합니다.

    예를 들어, 다음 URI는 계층 구조를 표현하며 orderId가 특정 사용자에 속한다는 것을 암시합니다.

    GET /users/{userId}/orders/{orderId}  // 권장
    GET /orders/{orderId}?userId=123     // 사용하지 말 것

    /, ?, #와 같은 예약된 문자는 정의된 역할에 따라 사용해야 합니다. 예

  3. 를 들어, / 문자는 계층적 구성 요소를 구분하는 데 사용되어야 하며, 쿼리 매개변수(?)는 비계층적 데이터 필터링 및 정렬 목적으로 사용되어야 합니다.

    // 계층적 관계를 나타내기 위해 '/'를 올바르게 사용:
    GET /users/123/orders/456  
    POST /books/789/reviews
    
    // 쿼리 매개변수를 지정하기 위해 '?'를 올바르게 사용:
    GET /products?category=electronics&sort=price_desc  
    
    // 프래그먼트 식별자를 위해 '#'를 올바르게 사용:
    GET /documentation#section2  

    일관된 소문자 URI는 시스템 간 호환성을 보장하기 위해 선호됩니다. 일부 환경에서는 URI를 대소문자를 구분하여 처리하기 때문에 대소문자를 혼용하면 오류가 발생할 가능성이 있습니다.

    http

    GET /categories/technology    // 사용
    GET /Categories/Technology   // 사용하지 말 것

    URI는 리소스를 나타내야 하며, 동작을 나타내지 않아야 합니다.

  4. 엔드포인트에 명사를 사용하고 동사를 피하세요. 이 구조는 리소스 지향 설계를 강조하는 HTTP 사양과 일치합니다.

    GET /books  				// 사용
    GET /getBooks			// 사용하지 말 것

HTTP 메서드를 올바르게 사용


  1. RFC 7231과 RFC 9110은 HTTP를 통해 요청할 수 있는 다양한 유형의 동작을 나타내기 위해 HTTP 메서드의 사용을 정의하고 논의합니다.

  2. 이러한 RFC는 또한 안전성, 멱등성, 캐시 가능성과 같은 속성과 그 목적에 대해 논의합니다.

    • - 멱등성(Idempotent): 동일한 요청을 여러 번 해도 단일 요청과 동일한 효과를 내는 메서드(예: PUT, DELETE).

    • - 안전성(Safe): 서버의 리소스를 수정하지 않는 메서드(예: GET, HEAD).

    • - 캐시 가능성(Cacheable): 응답이 향후 요청에 재사용될 수 있는 메서드(예: GET, HEAD).

무상태성(Statelessness)이 핵심


  1. 무상태성은 Roy Fielding의 박사 논문에서 설명한 REST 아키텍처 원칙에서 비롯된 것으로, REST의 지침 원칙 중 하나입니다.

  2. 무상태 API는 클라이언트가 서버로 보내는 각 요청이 서버가 요청을 이해하고 처리하는 데 필요한 모든 정보를 포함해야 함을 의미합니다.

  3. 서버는 요청 간에 클라이언트 컨텍스트를 저장하지 않아 확장성과 신뢰성이 향상됩니다. 클라이언트는 자신의 상태를 관리할 책임이 있습니다.

    예를 들어, 클라이언트가 여러 요청이 필요한 작업을 수행하려는 경우, 각 요청에 인증 자격 증명 및 기타 관련 데이터와 같은 모든 필요한 정보를 포함해야 합니다.

    무상태성은 서버 설계를 크게 단순화하고 무한한 확장성과 향상된 보안을 제공합니다.

  4. 반면, 상태 유지 설계는 서버가 클라이언트의 세션을 기억해야 할 수 있으며, 쿠키를 사용하여 사용자 상호 작용을 추적할 수 있어 확장성 문제를 초래할 수 있습니다.

표준 HTTP 응답 코드를 일관되게 사용


  1. HTTP 상태 코드는 요청이 성공했는지, 실패했는지 또는 클라이언트의 주의가 필요한 다른 조건이 발생했는지를 나타냅니다.

  2. 표준 HTTP 상태 코드를 사용하면 클라이언트가 그 의미에 따라 응답을 적절히 처리할 수 있습니다.

  3. 예를 들어, 성공적인 작업은 항상 200 OK를 반환해야 하며, 존재하지 않는 리소스에 대한 요청은 404 Not Found를 반환해야 합니다.

    다음 표는 RFC 7231 및 기타 권위 있는 출처를 기반으로 한 HTTP 상태 코드의 범주를 요약합니다:

API 버전 관리를 우아하게 처리


  1. REST API가 시간이 지남에 따라 발전할 때, 기존 클라이언트 애플리케이션을 깨뜨리지 않도록 하기 위해 하위 호환성을 유지하는 것이 중요합니다.

  2. API는 새로운 기능이나 변경 사항이 도입되더라도 클라이언트가 수정 없이 계속 작동할 수 있도록 설계되어야 합니다.

    REST API를 버전 관리하는 방법에는 여러 가지가 있습니다.

  3. 각 버전 관리 접근 방식에는 클라이언트가 API와 상호 작용하는 방식에 영향을 미치는 장단점이 있습니다. 가장 일반적인 버전 관리 접근 방식은 다음과 같습니다:

하위 호환성 보장


  1. 하위 호환성은 API 버전 관리와 관련된 개념입니다. API를 변경해야 할 때 버전 관리를 하며, 이때 API가 클라이언트 통합을 깨뜨릴 수 있습니다.

  2. 하위 호환성은 서버에 새로운 변경 사항이 배포되는 동안 기존 클라이언트 통합이 계속 작동하도록 보장합니다.

    Semantic Versioning 2.0.0에 따르면, 잘못 설계된 버전 관리 전략은 버전 잠금(Version Lock) 및/또는 버전 난잡성(Version Promiscuity)을 초래할 수 있습니다.

    • 버전 잠금: API의 버전 관리가 너무 엄격하여 클라이언트가 특정 버전의 API를 사용해야 하며 새로운 버전으로 쉽게 업그레이드할 수 없는 상황.

    • 버전 난잡성: API가 버전 관리에 있어 너무 많은 유연성을 허용하여 어떤 버전이 서로 호환되는지 명확히 지정하지 않는 상황.

    이러한 문제를 피하기 위해 다음 지침을 따라야 합니다:

    • 새로운 버전을 도입할 때, 이전 버전에 대한 명확한 사용 중단 타임라인을 제공하세요.

    • 이러한 변경 사항을 적절한 문서화와 API 호출에 대한 응답을 통해 전달하세요.

    • 이전 엔드포인트가 새로운 버전과 함께 계속 작동하도록 하위 호환성을 유지하세요.

    • 사용 중단된 기능과 마이그레이션 경로를 포함하여 버전 간 변경 사항을 설명하는 포괄적인 API 문서를 유지하세요.

    • Microsoft REST API 지침과 같은 리소스는 명확한 커뮤니케이션의 중요성을 강조합니다.

남용 방지를 위해 속도 제한 구현


  1. 속도 제한은 클라이언트가 지정된 시간 프레임 내에 요청을 얼마나 자주 할 수 있는지를 관리하여 API의 공정한 사용을 보장합니다.

  2. 속도 제한은 다양한 형태의 남용으로부터 API를 보호합니다:

    • 서비스 거부(DoS) 공격: 클라이언트가 할 수 있는 요청 수를 제한

    • 리소스 고갈: 속도 제한은 모든 클라이언트가 서버의 제한된 리소스에 공평하게 접근할 수 있도록 보장합니다

API 사용 모니터링 및 로깅을 통해 관찰 가능성과 문제 해결 지원


  1. API 요청 및 응답을 모니터링하고 추적하는 것은 API 성능, 사용 패턴 및 잠재적 문제에 대한 유용한 통찰력을 얻는 데 중요합니다. '

  2. 이를 위해 시장에는 여러 도구가 있으며, 이 특정 목적을 위해 구축된 솔루션을 사용하는 것이 매우 좋습니다. 일부 API 모니터링 도구는 다음과 같습니다:

    • - New Relic

    • - Datadog

    • - Prometheus 및 Grafana(오픈 소스)

    • - AWS CloudWatch

    • - Elastic APM

    • - Apigee

  3. 이러한 도구는 모니터링 및 정보 시각화에 도움이 되지만, 이러한 시스템에 올바른 정보를 제공하는 것이 중요합니다.

  4. 더 나은 모니터링을 위해 이러한 시스템이 사용할 수 있는 로그/이벤트를 생성하기 위해 다음 모범 사례를 따르세요:

    • - 구조화된 로깅 형식(예: JSON)을 사용하여 로그가 쉽게 검색 가능하고 모니터링 도구에서 파싱될 수 있도록 하세요.

    • - 다른 로그 수준(예: INFO, WARN, ERROR)을 구현하여 로그 항목을 심각도에 따라 분류하세요.

    • - 중앙 집중식 로깅 솔루션(예: ELK Stack, Splunk)을 사용하여 여러 소스의 로그를 집계하세요.

    • 실시간 모니터링 솔루션을 사용하여 API 상태 및 메트릭을 추적하고 잠재적 문제가 발생할 때 팀에 알리세요.

    • 로그 보존 정책을 명확히 설정하여 저장 비용을 관리하면서 로그가 충분한 시간 동안 사용 가능하도록 하여 트렌드 분석 및 문제 진단에 활용하세요.

성능 최적화를 위한 캐시 응답

  1. API에서 캐싱은 자주 요청되는 데이터를 저장하여 대기 시간을 줄이고 서버 부하를 감소시키며 응답 시간을 개선하는 데 도움을 줍니다.

  2. 캐싱은 사용자 프로필 정보, 국가 목록, 국가 내 주 목록, 지원되는 로케일 등과 같이 드물게 업데이트되는 데이터를 제공하는 API에서 유용합니다.

캐싱은 클라이언트 측, 서버 측, 중개 캐시(예: CDN) 등 다양한 수준에서 발생할 수 있습니다.

REST API의 경우 일반적으로 캐싱은 서버 측 캐싱을 의미하며, 동적 메커니즘을 통해 오래된 캐시를 무효화합니다.

여러 노드에서 애플리케이션을 배포할 계획이 있다면 서버 측 캐시 관리가 더 복잡해집니다.

RFC-9111에 명시된 바와 같이, REST API에서 캐싱은 Cache-Control, ETag, Last-Modified와 같은 HTTP 헤더에 의존합니다.

이러한 헤더는 클라이언트 또는 중개 캐시에서 응답을 저장하는 방법과 시점에 대한 지침을 제공합니다. 이러한 헤더와 캐싱 동작에 미치는 영향을 살펴보겠습니다:

필터링, 정렬 및 페이징 구현

  1. 일부 API는 매우 큰 데이터를 반환할 수 있습니다, 특히 GET API에서 그렇습니다. 성능을 향상시키고 사용자 경험을 개선하려면 필터링, 정렬 및 페이징을 구현하는 것이 중요합니다.

필터링은 클라이언트가 쿼리 매개변수를 지정하여 결과를 좁히도록 허용합니다. 예를 들어 ?status=active 또는 ?category=electronics 등입니다.

// 전자 제품 카테고리에서 Apple 제품만 가져오기 
GET /products?category=electronics&brand=apple

정렬은 사용자가 이름이나 created_at과 같은 필드를 기준으로 정렬된 결과를 가져올 수 있도록 합니다.

예를 들어 ?sort=name 또는 ?sort=-name과 같은 매개변수를 사용하여 오름차순 및 내림차순 정렬을 구현할 수 있습니다.

// 가격을 기준으로 오름차순 정렬된 제품 가져오기 
GET /products?sort=price

페이징은 한 번에 반환되는 데이터 양을 제어합니다.

일반적으로 limit 및 offset(또는 page와 size)을 지정하여 큰 데이터를 더 작은 청크로 나누어 클라이언트나 서버가 과부하되지 않도록 합니다.

// 세 번째 페이지의 결과를 가져옵니다. 각 페이지에는 10개의 항목이 있습니다.
GET /products?page=3&size=10

이러한 방법들을 결합하면 큰 데이터셋을 효과적으로 처리하고 사용자 경험을 개선할 수 있습니다.

Nordic의 RESTful API 페이징에 관한 글은 이 주제를 더 자세히 다룬 좋은 자료입니다.

API 보안은 후순위가 아니다

  1. API의 보안은 협상할 수 없는 중요한 사항입니다.

  2. 최신 보안 관행을 사용하고, OAuth2, API 키 또는 JWT(JSON Web Tokens)와 같은 적절한 인증 메커니즘을 사용해야 합니다.

  3. 요구 사항에 따라 역할 기반 접근 제어(RBAC)를 구현하여 사용자의 역할에 따라 특정 리소스에 대한 접근을 제한할 수 있습니다.

  • 사용자나 애플리케이션에 필요한 최소한의 권한만 부여하십시오. API 클라이언트에 과도한 권한을 부여하지 않도록 하세요.

  • HTTPS를 사용하여 전송 중인 데이터를 암호화하고 중간자 공격을 방지합니다. 노출 위험을 줄이기 위해 민감한 데이터를 암호화하고 개인 정보를 마스킹하는 것을 고려하십시오.

  • SQL 삽입 및 기타 공격으로부터 보호하려면 사용자 입력을 검증하고 정리하십시오. 각 쿼리 매개변수에 대해 명확한 데이터 유형과 제한을 정의하십시오.

위의 체크리스트 외에도 다음과 같은 관행을 따라야 합니다:

  • - 항상 SSL을 사용하십시오. 예외 없이.

  • - 이상한 패턴에 빠르게 대응할 수 있도록 알림을 설정하십시오.

  • - Content-Security-Policy, Strict-Transport-Security, X-Content-Type-Options와 같은 보안 헤더를 포함하여 특정 유형의 공격을 방지합니다.

  • - 오래된 API 버전을 주의 깊게 모니터링하고, 모든 활성 버전에는 정기적인 업데이트와 보안 패치를 제공해야 합니다.

훌륭한 문서화로 API 보완하기

  1. 간단히 말해서, API는 문서화만큼 훌륭합니다. 만약 API 사용에 대한 내부 문서가 부족하다면, 그 API를 사용하지 않는 것이 합리적일 수 있습니다.

API 문서는 잘 형식화되어 있어야 하며, 필요한 정보를 쉽게 찾을 수 있어야 합니다.

잘 알려진 문서화 플러그인이나 도구를 사용하는 것이 좋습니다. 이는 문서 정보를 탐색할 때 학습 곡선을 줄여주거나 없앨 수 있습니다.

좋은 문서는 항상 요청과 응답 예시로 가득 차 있습니다.

사용자가 API에 직접 게시할 수 있는 요청 샘플을 항상 사용하고, 인증/승인 문제 외에는 예상대로 작동하도록 하세요.

위의 글은 원문을 번역 및 재가공한 글로, 원문은 아래에서 확인하실 수 있습니다.

https://restfulapi.net/rest-api-best-practices/